package main

import "fmt"

/*
Golang 没有结构化异常，使用 panic 抛出错误，recover 捕获错误。

panic函数：
    1、内置函数
    2、假如函数F中书写了panic语句，会终止其后要执行的代码，在panic所在函数F内如果存在要执行的defer函数列表，按照defer的逆序执行
    3、返回函数F的调用者G，在G中，调用函数F语句之后的代码不会执行，假如函数G中存在要执行的defer函数列表，按照defer的逆序执行
    4、直到goroutine整个退出，并报告错误

recover函数
    1、内置函数，可以在defer调用完后程序不结束，主函数继续往后走。
    2、用来控制一个goroutine的panicking行为，捕获panic，从而影响应用的行为
    3、一般的调用建议
        a). 在defer函数中，通过recever来终止一个goroutine的panicking过程，从而恢复正常代码的执行
        b). 可以获取通过panic传递的error

1.利用recover处理panic指令，defer 必须放在 panic 之前定义，
	另外 recover 只有在 defer 调用的匿名函数中才有效。否则当panic时，recover无法捕获到panic，无法防止panic扩散。
2.recover 处理异常后，逻辑并不会恢复到 panic 那个点去，函数跑到 defer 之后的那个点。
3.多个 defer 会形成 defer 栈，后定义的 defer 语句会被最先调用。
*/

func main() {
	demo1RecoverHandlePanic()
	demo1RecoverNoPanic()
	demo2DeferHandleDefer()
	demo3RecoverValid()
	fmt.Println("main end...")
}

// recover函数使得main函数可以继续往后走
/*
demo1.....
开始处理demo1的defer。。。
程序执行出现异常！panic error!!!
*/
func demo1RecoverHandlePanic() {
	defer func() {
		fmt.Println("开始处理demo1的defer。。。")
		if err := recover(); err != nil { // 如果方法执行出现异常，这里的err := recover()就不是nil
			fmt.Println("程序执行出现异常！" + err.(string)) // 将 interface{} 转型为具体类型。
		}
	}()
	fmt.Println("demo1.....")
	panic("panic error!!!")
}

/*
hello demo1 normal;;;
开始处理demo1正常的defer。。。
*/
func demo1RecoverNoPanic() {
	defer func() {
		fmt.Println("开始处理demo1正常的defer。。。")
		if err := recover(); err != nil { // 方法执行正常，这里err就是nil
			fmt.Println(err)
		}
	}()
	fmt.Println("hello demo1 normal;;;")
}

//延迟调用中引发的错误，可被后续延迟调用捕获，但仅最后一个错误可被捕获。
func demo2DeferHandleDefer() {
	defer func() {
		fmt.Println(recover())
	}()

	defer func() {
		panic("defer panic")
	}()

	panic("test panic")
}

//捕获函数 recover 只有在延迟调用内直接调用才会终止错误，否则总是返回 nil。任何未捕获的错误都会沿调用堆栈向外传递
// 如果需要recover某一段函数，就需要构造一个内部函数来让defer调用
/*
defer inner
<nil>
demo3 test panic
*/
func demo3RecoverValid() {
	defer func() {
		fmt.Println(recover()) //有效
	}()
	defer recover()              //无效！
	defer fmt.Println(recover()) //无效！
	defer func() {
		func() {
			println("defer inner")
			recover() //无效！
		}()
	}()

	panic("demo3 test panic")
}
